rmic can now generate stubs for the Internet Inter-ORB protocol (IIOP) which is normally used by CORBA:
$ cd iiop/time $ CLASSPATH=../.. javac TimeService.java $ rmic -classpath ../.. -keepgenerated -iiop -d ../.. iiop.time.TimeService
Time and TimeService are unchanged except that the package is now iiop.time .
The proxy classes are _TimeService_Tie , required by the server, and _Time_Stub , required by server and client.
Client is changed because a reference to the service must now be obtained from a CORBA compliant name service:
package iiop.time; import iiop.rmi.Server; /** <tt>java iiop.time.Client</tt><br> connects to time service. */ public class Client { public static void main (String args []) { try { System.out.println(((Time)Server.lookup(Time.id, Time.class)).getTime()); } catch (Exception e) { System.err.println(e); System.exit(1); } } }
A new, reasonably generic rmi.Server class is implemented to support the lookup:
package iiop.rmi; import java.rmi.Remote; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; /** <tt>java iiop.rmi.Server service</tt><br> creates service(), binds it for service.id. Set <tt>rmi.log</tt> to display threads. */ public class Server { /** locates a service using JNDI. @param id service's registry name. */ public static Object lookup (String id, Class to) throws NamingException { return PortableRemoteObject.narrow(new InitialContext().lookup(id), to); }
This lookup uses the Java Naming and Directory Interface (JNDI) with a plugin for the CORBA naming service.
rmi.Server is designed to register and start a service:
/** starts and registers a service using JNDI. Set <tt>rmi.log</tt> to show threads. @param args [0] classname of service; class must contain static String id. */ public static void main (String args []) throws Exception { Class serviceClass = null; try { if (args != null && args.length > 0) { serviceClass = Class.forName(args[0]); Remote service = (Remote)serviceClass.newInstance(); PortableRemoteObject.exportObject(service); new InitialContext().rebind((String)serviceClass.getField("id").get(null), service); if (Boolean.getBoolean("rmi.log")) dumpThreads(true); } } finally { if (serviceClass == null) System.err.println("usage: java iiop.rmi.Server service"); } }
It is easy to see that InitialContext does for JNDI what Naming does for RMI. dumpThreads() is unchanged from before.
tnameserv implements a transient CORBA-compliant name service:
$ tnameserv -ORBInitialPort 1111 & Initial Naming Context: IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578743a312e3000000000010000000000000050000101000000000a3132372e302e302e3200042b00000018afabcafe00000002135175b00000000800000000000000000000000100000001000000140000000000010020000000000001010000000000 TransientNameServer: setting port for initial object references to: 1111 Ready.
The output could be used to provide a CORBA client with access to the name service. JNDI, however, can also deal with a URL-style reference:
$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 \ > iiop.rmi.Server iiop.time.TimeService & $ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 \ > iiop.time.Client Mon May 28 22:58:58 CEST 2001
Server and client need suitable JNDI plugins to reach tnameserv .
Both, the arithmetic service and the chat service, can be run over IIOP. However, the client has to do a little bit of extra work to set up the callback:
package iiop.cpu; import iiop.rmi.Server; import java.rmi.Remote; import java.rmi.RemoteException; import javax.rmi.PortableRemoteObject; /** <tt>java iiop.cpu.Client</tt><br> connects to cpu service, provides terms. */ public class Client implements Terms, Remote { // implementation of Terms as before public static void main (String args []) { try { Cpu cpu = (Cpu)Server.lookup(Cpu.id, Cpu.class); Client client = new Client(); PortableRemoteObject.exportObject(client); client.run(cpu); PortableRemoteObject.unexportObject(client); } catch (Exception e) { System.out.println(e); System.exit(1); } if (Boolean.getBoolean("rmi.log")) Server.dumpThreads(true); } }
Client implements Terms and needs to be exported by proxy to the arithmetic service. This requires essentially a delegate pattern which is managed using exportObject() and unexportObject() from PortableRemoteObject .
The threads are implemented correctly, the client terminates properly. However, the arithmetic service once again receives a stub rather than itself in result() :
$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 iiop.cpu.Client service: iiop.cpu.CpuService@4b222f != IOR:0000000000000029524d493a69696f702e6370752e437075536572766963653a3030303030303030303030303030303000000000000000010000000000000050000101000000000a3132372e302e302e3200043500000018afabcafe0000000213531d870000000800000001000000000000000100000001000000140000000000010020000000000001010000000000 5
Why should RMI be run over IIOP?
If RMI uses it's own protocol, it is more transparently integrated with Java.
IIOP must be used if a Java program wants to interact with another CORBA program, e.g., written in C++. This is pursued further in the next chapter.
RMI over IIOP is simpler to program than a CORBA application; therefore, this might be the best approach as long as there is no specific CORBA interface definition to fulfill.
[However, I have only managed to let the JDK 1.3.1 RMI over IIOP use the ORBacus C++ nameserv ; none of the other interoperations has worked.]